#include "battlefield.h"

#include <iostream>
#include <cstdlib>
#include <gl/gl.h>

#include "InputManager/keyboardmanager.h"
#include "graphicsmanager.h"
#include "random.h"
#include "Players\player.h"
#include "Enemies\enemy.h"
#include "statemanager.h"

using namespace std;

Battlefield::Battlefield() : Cursor({0, 0}), Units(), CursorMode(CursorModes::Free),
						MainMenuCursor(MainMenuOptions::EndTurn), CurrentTurn(Turns::Player),
						SelectedUnit(nullptr){
	for (int i = 0; i < Num_Tiles; i++){
		Tiles[i] = new Tile[Num_Tiles];
		for (int j = 0; j < Num_Tiles; j++){
			Tiles[i][j] = Tile();
		}
	}

	for (int i = Random::NextInt(10, 15); i >= 0; i--){
		Unit* p = Player::GetInstance();
		vector<int> Pos;
		while (true){
			Pos = {Random::NextInt(Num_Tiles), Random::NextInt(0, Num_Tiles/4)};
			if (!IsTileType(Pos, TileTypes::River) && GetUnitAt(Pos) == nullptr){
				break;
			}
		};
		p->Position = Pos;

		Units.push_back(p);
	}


	for (int i = Random::NextInt(10, 15); i >= 0; i--){
		Unit* e = Enemy::GetInstance();
		vector<int> Pos;
		while (true){
			Pos = {Random::NextInt(Num_Tiles), Random::NextInt((Num_Tiles * 3) / 4, Num_Tiles)};
			if (!IsTileType(Pos, TileTypes::River) && GetUnitAt(Pos) == nullptr){
				break;
			}
		};
		e->Position = Pos;
		Units.push_back(e);
	}

	GenerateRiver();
}

Battlefield::Battlefield(const Battlefield& other) : Cursor({0, 0}), Units(), CursorMode(other.CursorMode),
						MainMenuCursor(other.MainMenuCursor), CurrentTurn(other.CurrentTurn),
						SelectedUnit(nullptr){
	for (int i = 0; i < Num_Tiles; i++){
		Tiles[i] = new Tile[Num_Tiles];
		for (int j = 0; j < Num_Tiles; j++){
			Tiles[i][j] = other.Tiles[i][j];
		}
	}

	for (Unit *e : other.Units){
		Units.push_back(e);
	}
}

Battlefield& Battlefield::operator=(const Battlefield& other){
	Units = other.Units;
	CursorMode = other.CursorMode;
	MainMenuCursor = other.MainMenuCursor;
	CurrentTurn = other.CurrentTurn;
	SelectedUnit = nullptr;
	for (int i = 0; i < Num_Tiles; i++){
		Tiles[i] = new Tile[Num_Tiles];
		for (int j = 0; j < Num_Tiles; j++){
			Tiles[i][j] = other.Tiles[i][j];
		}
	}
	return *this;
}

void Battlefield::Update(){
	/* Remove the dead from the battlefield */{
		bool FoundFriendly = false;
		bool FoundEnemy = false;
		for (Unit* Loser : Units){
			if (Loser->Team == Teams::Enemy){
				FoundEnemy = true;
			} else {
				FoundFriendly = true;
			}
			if (Loser->IsAlive() == false){
				for (auto i = Units.begin(); i != Units.end(); i++){
					if (*i == Loser){
						Units.erase(i);
						break;
					}
				}
			}
		}

		if (!FoundEnemy || !FoundFriendly){
			StateManager::Instance->EndGame(FoundEnemy ? Teams::Enemy : Teams::Player);
		}
	}

	if (CurrentTurn == Turns::Player){
		/* Cursor Movement */ {
			if (CursorMode == CursorModes::Free || CursorMode == CursorModes::UnitSelected){
				if (KeyboardManager::IsPressed(VK_LEFT)){
					Cursor[0] -= 1;
				}
				if (KeyboardManager::IsPressed(VK_RIGHT)){
					Cursor[0] += 1;
				}
				if (KeyboardManager::IsPressed(VK_DOWN)){
					Cursor[1] += 1;
				}
				if (KeyboardManager::IsPressed(VK_UP)){
					Cursor[1] -= 1;
				}

				if (Cursor[0] < 0) Cursor[0] = 0;
				if (Cursor[0] >= Num_Tiles) Cursor[0] = Num_Tiles - 1;

				if (Cursor[1] < 0) Cursor[1] = 0;
				if (Cursor[1] >= Num_Tiles) Cursor[1] = Num_Tiles - 1;

			}
		}

		/* Moving Units */ {
			if (KeyboardManager::IsPressed(VK_SPACE) || KeyboardManager::IsPressed(VK_RETURN)){
				Unit* Entity = GetUnitAt(Cursor);
				if (CursorMode == CursorModes::Free && Entity != nullptr && Entity->Team == Teams::Player && Entity->Movement.Current > 0){
					CursorMode = CursorModes::UnitSelected;
					SelectedUnit = Entity;
				} else if (CursorMode == CursorModes::UnitSelected && UnitCanMoveTo(SelectedUnit, Cursor) &&
							(Entity == nullptr || Entity->Team == Teams::Enemy)){
					SelectedUnit->Movement.Current -= Distance(Cursor, SelectedUnit->Position);
					SelectedUnit->Position = Cursor;
					CursorMode = CursorModes::Free;
					if(Entity != nullptr){
						StateManager::Instance->StartBattle((Player*)SelectedUnit, (Enemy*)Entity);
					}
				}
			}
		}

		/* Handle Escape Button Navigation */ {
			if (KeyboardManager::IsPressed(VK_ESCAPE)){
				if (CursorMode == CursorModes::UnitSelected){
					CursorMode = CursorModes::Free; //Cancels unit selection
				} else if (CursorMode == CursorModes::Free){
					CursorMode = CursorModes::MainMenu; //Opens up the escape menu
					MainMenuCursor = (MainMenuOptions)0;
				} else if (CursorMode == CursorModes::MainMenu){
					CursorMode = CursorModes::Free; //Cancels out of it
				}
			}
		}

		/* Handle Escape Menu Navigation */ {
			if (CursorMode == CursorModes::MainMenu){
				if (KeyboardManager::IsPressed(VK_UP)){
					MainMenuCursor = (MainMenuOptions)((int)MainMenuCursor-1);
				} else if (KeyboardManager::IsPressed(VK_DOWN)){
					MainMenuCursor = (MainMenuOptions)((int)MainMenuCursor+1);
				}

				/* Keep it within bounds */
				if ((int)MainMenuCursor < 0){
					MainMenuCursor = (MainMenuOptions)((int)MainMenuOptions::Size - 1);
				} else if (MainMenuCursor >= MainMenuOptions::Size){
					MainMenuCursor = (MainMenuOptions)0;
				}

				if (KeyboardManager::IsPressed(VK_SPACE) || KeyboardManager::IsPressed(VK_RETURN)){
					if (MainMenuCursor == MainMenuOptions::Cancel){
					} else if (MainMenuCursor == MainMenuOptions::QuitGame){
						StateManager::Quit();
					} else if (MainMenuCursor == MainMenuOptions::EndTurn){
						EndTurn();
					}
					CursorMode = CursorModes::Free;
				}
			}
		}
	} else {
		/* Move AI units */
		bool MovedUnit = false;
		for (Unit* u : Units){
			if (u->Team == Teams::Enemy && u->Movement.Current != 0){
				MoveEnemy((Enemy*)u);
				MovedUnit = true;
				break;
			}
		}
		if (!MovedUnit){
			EndTurn();
		}
	}
}

void Battlefield::EndTurn(){
	if (CurrentTurn == Turns::Player){
		/* Reset Movement Points */
		for (auto i = Units.begin(); i != Units.end(); i++) {
			(*i)->Movement.Current = (*i)->Movement.Maximum;
		}

		CurrentTurn = Turns::Enemy;
	} else {
		/* Reset Movement Points */
		for (auto i = Units.begin(); i != Units.end(); i++) {
			(*i)->Movement.Current = (*i)->Movement.Maximum;
		}

		CurrentTurn = Turns::Player;
	}
}

int Battlefield::DistanceToClosestPlayer(vector<int> pos){
	int BestDist = 999;
	for (Unit* u : Units){
		int Dist = Distance(pos, u->Position);
		if (Dist < BestDist && u->Team == Teams::Player){
			BestDist = Dist;
		}
	}
	return BestDist;
}

void Battlefield::MoveEnemy(Enemy* e){
	vector<vector<int>> PotentialPositions;
	for (int x = 0; x < Num_Tiles; x++){
		for (int y = 0; y < Num_Tiles; y++){
			if (UnitCanMoveTo(e, {x,y})){
				PotentialPositions.push_back({x,y});
			}
		}
	}

	vector<int> BestPosition = e->Position;
	int BestDist = 999;
	for (vector<int> position : PotentialPositions){
		int Dist = DistanceToClosestPlayer(position);
		if (Dist < BestDist){
			BestDist = Dist;
			BestPosition = position;
		}
	}

	Unit* Target = GetUnitAt(BestPosition);
	if (Target == nullptr){
		e->Position = BestPosition;
	} else if (Target->Team == Teams::Player){
		e->Position = BestPosition;
		StateManager::Instance->StartBattle((Player*)Target, e);
	}
	e->Movement.Current = 0;
}

void Battlefield::Draw(){
	/* Draw Map */{
		GraphicsManager::DrawRectangle(0, 0, 800, 600, Color(1,1,1));
		for (int i = 0; i < Num_Tiles; i++){
			for (int j = 0; j < Num_Tiles; j++){
				Tiles[i][j].Draw(i, j, Tile_Size, Tile_Size);

				if (CursorMode == CursorModes::UnitSelected && UnitCanMoveTo(SelectedUnit, {i, j})){
					GraphicsManager::DrawRectangle(i * Tile_Size, j * Tile_Size, Tile_Size, Tile_Size, Color(1, 1, 1, 0.4));
				}
			}
		}
	}

	/* Draw Players */ {
		for (Unit *e : Units){
			if (CursorMode == CursorModes::UnitSelected && SelectedUnit == e){
				GraphicsManager::DrawRectangle(e->Position[0] * Tile_Size, e->Position[1] * Tile_Size,
												Tile_Size, Tile_Size, Color(1, 1, 0, 0.75));
			}
			e->BattlefieldDraw();
		}
	}

	/* Draw Grid Lines */{
		for (int i = 0; i <= Num_Tiles; i++){
			glBegin(GL_LINES);{
				glColor3d(0.0, 0.0, 0.0);
				glVertex2d(i * Tile_Size, 0);
				glVertex2d(i * Tile_Size, Num_Tiles * Tile_Size);
			}glEnd();

			glBegin(GL_LINES);{
				glColor3d(0.0, 0.0, 0.0);
				glVertex2d(0, i * Tile_Size);
				glVertex2d(Num_Tiles * Tile_Size, i * Tile_Size);
			}glEnd();
		}
	}

	/* Draw Cursor */{
		float CursorSize = 2.5;
		float CursorLength = Tile_Size + (CursorSize * 2);
		double TLCorner[] = {(Cursor[0] * Tile_Size) - CursorSize, (Cursor[1] * Tile_Size) - CursorSize};
		double BLCorner[] = {(Cursor[0] * Tile_Size) - CursorSize, ((Cursor[1] + 1) * Tile_Size)- CursorSize};
		double TRCorner[] = {((Cursor[0] + 1) * Tile_Size) - CursorSize, (Cursor[1] * Tile_Size) - CursorSize};

		Color Black = Color(0, 0, 0);

		//Top
		GraphicsManager::DrawRectangle(TLCorner[0], TLCorner[1],
			CursorSize * 2, CursorLength, Black);
		//Left
		GraphicsManager::DrawRectangle(TLCorner[0], TLCorner[1],
			CursorLength, CursorSize * 2, Black);
		//Bottom
		GraphicsManager::DrawRectangle(BLCorner[0], BLCorner[1],
			CursorLength, CursorSize * 2, Black);
		//Right
		GraphicsManager::DrawRectangle(TRCorner[0], TRCorner[1],
			CursorSize * 2, CursorLength, Black);
	}

	/* Current Position Info */{
		if (IsValidPosition(Cursor)){
			Tile tile = Tiles[Cursor[0]][Cursor[1]];
			if (tile.TileType == TileTypes::Grass){
				GraphicsManager::DrawString((Num_Tiles * Tile_Size) + 5, 5, "Grass");
			} else if (tile.TileType == TileTypes::River){
				GraphicsManager::DrawString((Num_Tiles * Tile_Size) + 5, 5, "River");
			} else if (tile.TileType == TileTypes::Mountain){
				GraphicsManager::DrawString((Num_Tiles * Tile_Size) + 5, 5, "Mountain");
			}

			Unit *unit = GetUnitAt(Cursor);
			if (unit != nullptr){
				if (unit->Team == Teams::Player){
					GraphicsManager::DrawString((Num_Tiles * Tile_Size) + 5, 5 + 16, "Ally");
				} else if (unit->Team == Teams::Enemy){
					GraphicsManager::DrawString((Num_Tiles * Tile_Size) + 5, 5 + 16, "Enemy ");
				}
				GraphicsManager::DrawString((Num_Tiles * Tile_Size) + 5 + 16, 5 + (16 * 2), unit->Name);

				string Speed = "Speed: ";
				Speed += to_string(unit->Movement.Current);
				Speed += "/";
				Speed += to_string(unit->Movement.Maximum);
				GraphicsManager::DrawString((Num_Tiles * Tile_Size) + 5 + 16, 5 + (16 * 3), Speed);

				string HP = "HP: ";
				HP += to_string(unit->HP.Current);
				HP += "/";
				HP += to_string(unit->HP.Maximum);
				GraphicsManager::DrawString((Num_Tiles * Tile_Size) + 5 + 16, 5 + (16 * 4), HP);

				string MP = "MP: ";
				MP += to_string(unit->MP.Current);
				MP += "/";
				MP += to_string(unit->MP.Maximum);
				GraphicsManager::DrawString((Num_Tiles * Tile_Size) + 5 + 16, 5 + (16 * 5), MP);
			}
		}
	}

	/* Draw Main Menu */ {
		if (CursorMode == CursorModes::MainMenu){
			GraphicsManager::DrawRectangle(100, 100, 16 * 10, 16*3, Color(0,0,0));
			GraphicsManager::DrawString(100 + 16, 100, "End Turn");
			GraphicsManager::DrawString(100 + 16, 100 + 16, "Quit Game");
			GraphicsManager::DrawString(100 + 16, 100 + (16*2), "Cancel");

			GraphicsManager::DrawString(100, 100 + ((int)MainMenuCursor * 16), ">");
		}
	}
}

Unit* Battlefield::GetUnitAt(vector<int> position){
	int x = position[0];
	int y = position[1];
	for (int i = 0; i < (int)Units.size(); i++){
		Unit *p = Units[i];
		if (p->Position[0] == x && p->Position[1] == y){
			return Units[i];
		}
	}
	return nullptr;
}

int Battlefield::Distance(vector<int> p1, vector<int> p2){
	return abs(p1[0] - p2[0]) + abs(p1[1] - p2[1]);
}

bool Battlefield::UnitCanMoveTo(Unit* unit, vector<int> Position){
	if (IsTileType(Position, TileTypes::River)) return false;
	if (GetUnitAt(Position) != nullptr){
		if (GetUnitAt(Position)->Team == unit->Team) return false;
	}
	return Distance(unit->Position, Position) <= unit->Movement.Current;
}

/* River Generation */
void Battlefield::SetTile(vector<int> Position, TileTypes Type){
	if (IsValidPosition(Position)){
		Tiles[Position[0]][Position[1]].TileType = Type;
	}
}

bool Battlefield::IsTileType(vector<int> Position, TileTypes type){
	if (IsValidPosition(Position)){
		return Tiles[Position[0]][Position[1]].TileType == type;
	} else {
		return false;
	}
}

bool Battlefield::IsValidPosition(vector<int> Position){
	return (Position[0] >= 0 && Position[0] < Num_Tiles &&
			Position[1] >= 0 && Position[1] < Num_Tiles);
}

vector<vector<int>> Battlefield::GetAdjacentPositions(vector<int> Position){
	return {
		{Position[0] - 1, Position[1]},
		{Position[0] + 1, Position[1]},
		{Position[0], Position[1] - 1},
		{Position[0], Position[1] + 1},
	};
}

void Battlefield::GenerateRiver(){
	//Pick a random starting point along left edge
	vector<int> RiverPos = {1, Random::NextInt(0, Num_Tiles)};

	//Color starting point and tile next to point along edge a river
	SetTile({0, RiverPos[1]}, TileTypes::River);
	SetTile(RiverPos, TileTypes::River);

	//Start generating a river (no more than 1000 in length)
	for (int i = 0; i < 1000; i++){
		//Get a list of adjacent tiles, and start making space for them
		vector<vector<int>> Adjacent = GetAdjacentPositions(RiverPos);
		vector<vector<int>> Candidates;

		//Remove adjacent tiles with too many rivers nearby
		for (int k = 0; k < 4; k++){
			vector<vector<int>> Nearby = GetAdjacentPositions(Adjacent[k]);
			int cnt = 0;
			for (vector<int> tile : Nearby){
				if (IsTileType(tile, TileTypes::River)){
					cnt++;
				}
			}
			if (cnt <= 1 && IsValidPosition(Adjacent[k]) && !IsTileType(Adjacent[k], TileTypes::River)){
				Candidates.push_back(Adjacent[k]);
			}
		}

		//If we have no candidates, simply break
		if (Candidates.size() == 0){
			break;
		}

		//Pick a candidate randomly
		vector<int> Winner = Candidates[Random::NextInt(Candidates.size())];
		RiverPos = Winner;
		SetTile(RiverPos, TileTypes::River);

		//Once we hit another edge of the map, end the generation
		if (RiverPos[0] == 0 || RiverPos[0] == Num_Tiles - 1 ||
			RiverPos[1] == 0 || RiverPos[1] == Num_Tiles - 1){
			break;
		}
	}
}


